Skip to content

fix(responses): enforce required tools in native combo streams - #7831

Merged
lidge-jun merged 8 commits into
devfrom
fix/7456-stream-required-tool-failover
Oct 28, 2026
Merged

lidge-jun merged 8 commits into
devfrom
fix/7456-stream-required-tool-failover

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

A streaming Responses request with tool_choice: "required" (or a named function) routed through a combo could end as response.completed with zero calls when the selected native child ignored the tool contract, and the combo treated that as success. The buffered half was fixed in #7741 and translated Chat in #7463; this covers native SSE combo children.

For those children only, the required-tool contract is now checked on the stream:

  • If the child completes without a required call and nothing client-visible has been committed (only lifecycle or reasoning events were held), the combo moves to its next eligible target. No bytes from the discarded child reach the client.
  • If text, a tool event, hosted-tool execution, or any ambiguous output was already committed, the request ends with a typed response.failed (required_tool_contract_unsatisfied) instead of a false completed. There is no replay or hop after commitment, and completion callbacks and continuation storage do not fire.
  • Raw inspection and client restoration resolve named selectors and observed calls through the same authorized namespace restorers, so mcp.ping and mcp__ping echoes produce one consistent decision.
  • Holding reasoning while the contract is open is bounded: after 1,000 ms from the first held frame the child is committed and the in-flight read is handed to the relay, so nothing is lost and no hop is authorized.

Non-required requests, direct native requests, translated Chat, runTurn and WebSocket paths are unchanged.

Refs #7456 (streaming half).

Verification

  • tests/server/server-combo-stream-required-tool.test.ts and tests/routing/combo-required-tool-stream-preflight.test.ts (new): required and named choices, held vs committed output, typed failure with one send, hosted-tool effects, call-ID ownership, the 151-tool fallback/call/result cycle, eight eager/tee alias combinations, fake-clock hold expiry (silence, trickle, cancellation, byte/chunk caps). Before the source change: 4 pass / 9 fail; before the round-1 fixes: 42 pass / 8 fail.
  • On the PR head (OCX_TEST_NO_QUEUE=1, Bun 1.4.4), 12 files including the existing combo preflight, zero-output failover, tool-loop continuation, buffered contract, Chat required-tool, layout, file-size ratchet and Lab boundary tests: 250 pass / 0 fail.
  • bun run typecheck, bun run structure:check, bun run privacy:scan, git diff --check: pass.
  • Two independent review rounds: round 1 found two blockers (alias disagreement, unbounded reasoning hold), both fixed; round 2 PASS with 0 blocking findings.
  • Not run locally: full bun run test and test:changed (shared host under load); left to CI.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes

    • Streaming requests that require a tool can try another eligible target when the first target returns only buffered text or reasoning without making the required call. The reasoning hold is limited to one second.
    • Once response content is committed, a missing required tool call returns a clear failure instead of replaying the request. Existing output and usage are preserved.
    • Hosted tool execution and unsupported output prevent unsafe retries; hosted substitutions fulfill a required choice only when they apply to the request actually sent.
  • Documentation

    • Clarified required-tool behavior for streaming Responses combo requests, including how it differs from direct Responses and translated Chat requests.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner October 27, 2026 15:24
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T15:26:57.963498Z 6b94742 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between a968ad6 and 611cb86.

Files selected for processing: 1
  • tests/helpers/responses-core-source.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Walkthrough

Native Responses combo streams now enforce required-tool contracts. Preflight holds reasoning frames for up to one second before committing the target. A missing call can trigger failover before commitment; after commitment, the stream returns a typed failure without replaying delivered output.

Changes: Native Responses streaming

Layer / File(s) Summary
Required-tool fulfillment and hosted substitutions
src/adapters/base.ts, src/adapters/openai-responses/image-gen.ts, src/adapters/openai-responses/passthrough.ts, src/server/responses/combo-required-tool-fulfillments.ts, src/server/responses-image-gen-repair.ts, structure/data-planes/images.md, tests/server/server-combo-stream-send-contract.test.ts
Adapter requests record hosted image-tool substitutions. Required-tool fulfillment checks caller intent against the outbound request. Tests cover substitutions in queued and preset requests, including changed provider configuration.
Required-tool stream guard and delivery
src/server/responses/combo-required-tool-stream.ts, src/server/responses/passthrough-delivery.ts, tests/server/server-combo-stream-required-tool.test.ts, tests/server/server-combo-stream-hosted-tool-contract.test.ts, tests/server/server-combo-required-tool-contract.test.ts, tests/helpers/responses-core-source.ts
The guard inspects normalized output and rewrites qualifying missing-call completions to response.failed. Passthrough delivery uses the guard for eligible combo streams. Server tests cover required-call matching, failover, committed output, hosted calls, and cancellation.
Bounded preflight and combo integration
src/server/responses/combo-stream-preflight.ts, src/server/responses/core-combo.ts, tests/routing/combo-required-tool-stream-preflight.test.ts
Preflight holds reasoning frames for up to one second, preserves an in-flight read, and handles cancellation. Combo execution supplies required-tool and abort options. Routing tests cover deadlines, commitment, and cancellation.
Streaming contract and test coverage
docs-site/src/content/docs/reference/proxy-formats.md, structure/transports/responses-combo-boundaries.md, structure/transports/responses-failover.md, structure/transports/responses.md, scripts/test-layout/layout.json, scripts/test-layout/layout-combos.json, tests/fixtures/test-layout-expected.json
Documentation describes the streaming contract and distinguishes it from direct native and translated Chat routes. Test-layout files register the added suites.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~30 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant executeComboResponses
  participant preflightComboStreamResponse
  participant deliverPassthroughResponse
  participant createComboRequiredToolStreamGuard
  participant Client
  executeComboResponses->>preflightComboStreamResponse: provide child response and required-tool prelude option
  preflightComboStreamResponse->>deliverPassthroughResponse: accept response after commitment or deadline
  deliverPassthroughResponse->>createComboRequiredToolStreamGuard: inspect and rewrite normalized stream events
  createComboRequiredToolStreamGuard->>Client: forward stream or typed failure
Loading

Possibly related PRs

  • lidge-jun/opencodex#7741: Implements buffered native Responses required-tool validation that this change extends to streaming combo children.
  • lidge-jun/opencodex#7711: Adds buffered required-tool validation and typed failures, which this change extends to native SSE combo children.
Merge Risk: Minimal · up to 611cb

No merge-blocking issue was identified in the reviewed change.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: enforcing required tools for native Responses combo streams. It matches the documented implementation and PR objective.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 23 functions across 15 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b94742e3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Oct 27, 2026
passthrough-delivery.ts now reaches combo-required-tool-stream.ts and
combo-required-tool-fulfillments.ts, so the core module boundary test expects
both in RESPONSES_CORE_MODULES, which also puts them under its line cap.
@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer dev integration decision (per MAINTAINERS.md).

  • Exact head: 611cb862233572d2b98b5a2fb3cfff3c543321c6. All 30 checks on this head passed, including Cross-platform CI with gates and all four Ubuntu test shards. An earlier head failed shard 4/4 on the Responses core module boundary test because the two new owners were not registered; 611cb8622 registers them. macOS/Windows jobs are skipped on pull requests by policy.
  • Review: five independent rounds.
    • Round 1 found an alias disagreement and an unbounded reasoning hold.
    • The Codex review found that authorized hosted-image substitution was rejected.
    • Round 3 found a stale outbound snapshot after queued rebuilds and contract weakening under capability filtering.
    • Every finding was fixed with red/green regressions. Rounds 2, 4 and 5 passed with 0 blocking findings; round 5 covers this exact head.
  • Merge check: dev changed passthrough.ts, core-combo.ts and responses-core-source.ts since the base. The merged tree was built locally and verified: 250 combo, required-tool, preflight, core-module, layout, file-size and Lab-boundary tests and 288 core-source consumer and passthrough tests pass, plus typecheck and structure:check. responses.md and responses-failover.md stay at 600 lines and layout.json at 1999.

@lidge-jun
lidge-jun merged commit 8145d52 into dev Oct 28, 2026
30 checks passed
@lidge-jun
lidge-jun deleted the fix/7456-stream-required-tool-failover branch October 28, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant